package com.apobates.forum.toro.tuple.strategy;

import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 抽像的Many Pair 异步策略
 * @param <L> 左对象类型
 * @param <R> 右对象类型
 * @param <U> 右对象查询函数参数的类型
 *
 * @author xiaofanku@live.cn
 * @since 20220517
 */
public abstract class AbstractManyPairFutureStrategy<L,R,U> extends AbstractTupleStrategy {
    private final int timeout;
    private final TimeUnit unit;
    private final static Logger logger = LoggerFactory.getLogger(AbstractManyPairFutureStrategy.class);

    public AbstractManyPairFutureStrategy(int timeout, TimeUnit unit) {
        this.timeout = timeout;
        this.unit = unit;
    }

    public AbstractManyPairFutureStrategy() {
        this.timeout = 1;
        this.unit = TimeUnit.SECONDS;
    }
    protected String getSentinel(){
        return "ManyPair";
    }
    public int getTimeout() {
        return timeout;
    }

    public TimeUnit getUnit() {
        return unit;
    }

    /**
     * 返回聚合结果
     * 注: 找到第一个满足条件的右对像后停止搜索其它
     * @param lFuture 左对象的Future实例
     * @param rightFun 右对象查询函数
     * @param argumentMap 连接参数集合
     * @return
     */
    protected Collection<Pair<L, R>> get(
            CompletableFuture<Collection<L>> lFuture,
            Function<Collection<U>, Collection<R>> rightFun,
            Map<String, String> argumentMap){
        // 左右
        CompletableFuture<Pair<Collection<L>, Collection<R>>> pairFuture = lFuture.thenCompose((Collection<L> leftObjSet) -> CompletableFuture.supplyAsync(() -> {
            // 取得右对象的查询参数
            Set<U> rightArgs = leftObjSet.stream().map(ele -> {
                Object selectRightVal = getBaseSelectVal(ele, argumentMap.get("rightAttr"), argumentMap.get("rightAttrType"), "右");
                try {
                    return (U) selectRightVal;
                }catch (Exception e){
                    logger.debug(String.format("[TS][ManyPair]右函数的查询参数时出现异常: %s", e.getMessage()));
                    return null;
                }
            }).filter(Objects::nonNull).collect(Collectors.toSet());
            // logger.debug("[Many-Pair]右对象查询参数数量:"+rightArgs.size());
            Collection<R> rightObjSet = rightFun.apply(rightArgs);
            // logger.debug("[Many-Pair]右对象查询数量:"+rightObjSet.size());
            return Pair.of(leftObjSet, rightObjSet);
        })).completeOnTimeout(null, this.timeout, this.unit);
        // 折叠
        return pairFuture.thenApply((Pair<Collection<L>, Collection<R>> p) -> {
            return foldPair(p.getLeft(), p.getRight(), argumentMap);
        }).completeOnTimeout(Collections.emptyList(), this.timeout, this.unit).orTimeout(this.timeout, this.unit).join();
    }

    /**
     * 返回聚合结果
     * 注: 返回所有满足条件的右对像
     * @param lFuture 左对象的Future实例
     * @param rightFun 右对象查询函数
     * @param argumentMap 连接参数集合
     * @return
     */
    protected Collection<Pair<L, Collection<R>>> getAny(
            CompletableFuture<Collection<L>> lFuture,
            Function<Collection<U>, Collection<R>> rightFun,
            Map<String, String> argumentMap){
        // 左右
        CompletableFuture<Pair<Collection<L>, Collection<R>>> pairFuture = lFuture.thenCompose((Collection<L> leftObjSet) -> CompletableFuture.supplyAsync(() -> {
            // 取得右对象的查询参数
            Set<U> rightArgs = leftObjSet.stream().map(ele -> {
                Object selectRightVal = getBaseSelectVal(ele, argumentMap.get("rightAttr"), argumentMap.get("rightAttrType"), "右");
                try {
                    return (U) selectRightVal;
                }catch (Exception e){
                    logger.debug(String.format("[TS][ManyPair]右函数的查询参数时出现异常: %s", e.getMessage()));
                    return null;
                }
            }).filter(Objects::nonNull).collect(Collectors.toSet());
            // logger.debug("[Many-Pair]右对象查询参数数量:"+rightArgs.size());
            Collection<R> rightObjSet = rightFun.apply(rightArgs);
            // logger.debug("[Many-Pair]右对象查询数量:"+rightObjSet.size());
            return Pair.of(leftObjSet, rightObjSet);
        })).completeOnTimeout(null, this.timeout, this.unit);
        // 折叠
        return pairFuture.thenApply((Pair<Collection<L>, Collection<R>> p) -> {
            return foldPairAny(p.getLeft(), p.getRight(), argumentMap);
        }).completeOnTimeout(Collections.emptyList(), this.timeout, this.unit).orTimeout(this.timeout, this.unit).join();
    }
}
